#include "D3DApp.h"
D3dApp*				g_pD3dApp = 0;
IDirect3DDevice9*	g_pD3dDevice = 0;
LRESULT CALLBACK WndProc(HWND p_hWnd, UINT p_uMsg, WPARAM p_wParam, LPARAM p_lParam)
{
	if (g_pD3dApp)
	{
	 	return g_pD3dApp->msgProc(p_uMsg, p_wParam, p_lParam);
	}
	else
	{
		return DefWindowProc(p_hWnd, p_uMsg, p_wParam, p_lParam);
	}
}
D3dApp::D3dApp(HINSTANCE p_hInstance, LPSTR p_sName, D3DDEVTYPE p_devType) : 
	m_pD3dObj(Direct3DCreate9(D3D_SDK_VERSION)), 
	m_hInstance(p_hInstance),
	m_sName(p_sName),
	m_devType(p_devType)
{
	if (isInCompatible())
	{
		MessageBox(m_hWnd, TEXT("Incompatible Hardware!"), TEXT("ERROR"), MB_OK);
		PostQuitMessage(0);
		return;
	}
	if (!initWindow() || !initDirectX())
	{
		MessageBox(m_hWnd, TEXT("ERROR"), TEXT("ERROR"), MB_OK);
		PostQuitMessage(0);
		return;
	}
}
D3dApp::~D3dApp()
{
	if (g_pD3dDevice)
	{
		g_pD3dDevice->Release();
		g_pD3dDevice = 0;
	}
	if(m_pD3dObj)
	{
		m_pD3dObj->Release();
		m_pD3dObj = 0;
	}
}
bool D3dApp::isInCompatible()
{
	D3DDISPLAYMODE mode;
	m_pD3dObj->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &mode);
	m_supportedDASM = DA_SCREENMODE_NONE;
	if (m_pD3dObj->CheckDeviceType(D3DADAPTER_DEFAULT, m_devType, mode.Format, mode.Format, true) == D3D_OK)
	{
		m_supportedDASM |= DA_SCREENMODE_WINDOWED;
	}
	if(m_pD3dObj->CheckDeviceType(D3DADAPTER_DEFAULT, m_devType, mode.Format, mode.Format, false) == D3D_OK)
	{
		m_supportedDASM |= DA_SCREENMODE_FULLSCREEN;
	}
	if (m_supportedDASM == DA_SCREENMODE_NONE)
	{
		return true;
	}
	D3DCAPS9 caps;
	m_pD3dObj->GetDeviceCaps(D3DADAPTER_DEFAULT, m_devType, &caps);
	m_vpMode = 0;
	if (caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)
	{
		m_vpMode |= D3DDEVCAPS_HWTRANSFORMANDLIGHT;
	}
	else
	{
		m_vpMode |= D3DCREATE_SOFTWARE_VERTEXPROCESSING;
	}
	if(caps.DevCaps & D3DDEVCAPS_PUREDEVICE & m_vpMode& D3DCREATE_HARDWARE_VERTEXPROCESSING)
	{
		m_vpMode |= D3DDEVCAPS_PUREDEVICE;
	}
	return m_vpMode == 0;
}
bool D3dApp::initWindow()
{
	WNDCLASSEX wc;
	wc.cbClsExtra = 0;
	wc.cbSize = sizeof(wc);
	wc.cbWndExtra = 0;
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.hCursor = LoadCursor(m_hInstance, IDC_ARROW);
	wc.hIcon = LoadIcon(m_hInstance, IDI_APPLICATION);
	wc.hIconSm = LoadIcon(m_hInstance, IDI_APPLICATION);
	wc.hInstance = m_hInstance;
	wc.lpfnWndProc = WndProc;
	wc.lpszClassName = m_sName;
	wc.lpszMenuName = 0;
	wc.style = CS_HREDRAW | CS_VREDRAW;
	if (RegisterClassEx(&wc))
	{
		m_hWnd = CreateWindow(wc.lpszClassName, wc.lpszClassName, WS_OVERLAPPED | WS_VISIBLE, 0, 0, DA_SCREENWIDTH_DEFAULT, DA_SCREENHEIGHT_DEFAULT, HWND_TOP, NULL, m_hInstance, 0);
		if (m_hWnd)
		{
			return true;
		}
	}
	return false;
}
bool D3dApp::initDirectX()
{
	ZeroMemory(&m_d3dpp, sizeof(m_d3dpp));
	m_d3dpp.BackBufferCount = 1;
	m_d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
	m_d3dpp.BackBufferWidth = DA_SCREENWIDTH_DEFAULT;
	m_d3dpp.BackBufferHeight = DA_SCREENHEIGHT_DEFAULT;
	
	m_d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;
	m_d3dpp.MultiSampleQuality = 0;
	m_d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	m_d3dpp.hDeviceWindow = m_hWnd;

	m_d3dpp.Windowed = true;
	
	m_d3dpp.EnableAutoDepthStencil = true;
	m_d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;

	m_d3dpp.Flags = 0;
	m_d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
	m_d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;

	DWORD rs = m_pD3dObj->CreateDevice(D3DADAPTER_DEFAULT, m_devType, m_hWnd, m_vpMode, &m_d3dpp, &g_pD3dDevice);
	return g_pD3dDevice != 0;
}
HWND D3dApp::getWnd()
{
	return m_hWnd;
}
HINSTANCE D3dApp::getInstance()
{
	return m_hInstance;
}
void D3dApp::draw()
{

}
void D3dApp::update(float p_fDt)
{

}
int D3dApp::run()
{
	MSG msg;
	msg.message = WM_NULL;
	ShowWindow(m_hWnd, SW_NORMAL);
	UpdateWindow(m_hWnd);
	__int64 cps = 0;
	QueryPerformanceFrequency((LARGE_INTEGER *)&cps);
	float spc = 1.0f/cps;
	__int64 t0 = 0;
	QueryPerformanceCounter((LARGE_INTEGER *)&t0);
	__int64 t1 = 0;
	do
	{
		if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		else
		{
			if (isDeviceReady())
			{
				QueryPerformanceCounter((LARGE_INTEGER *)&t1);
				update((t1 - t0)*spc);
				RECT rect;
				GetWindowRect(m_hWnd, &rect);
				draw();			
				QueryPerformanceCounter((LARGE_INTEGER *)&t0);
			}
		}
	} while (msg.message != WM_QUIT);
	return msg.wParam;
}

LRESULT D3dApp::msgProc(UINT p_uMsg, WPARAM p_wParam, LPARAM p_lParam)
{
	switch(p_uMsg)
	{
	case WM_SIZE:
	case WM_EXITSIZEMOVE:
		RECT rect;
		GetClientRect(m_hWnd, &rect);
		m_d3dpp.BackBufferWidth = rect.right - rect.left;
		m_d3dpp.BackBufferHeight = rect.bottom - rect.top;
		onLostDevice();
		g_pD3dDevice->Reset(&m_d3dpp);
		onResetDevice();
		return 0;
	case WM_CLOSE:
		DestroyWindow(m_hWnd);
		return 0;
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	default:
		return DefWindowProc(m_hWnd, p_uMsg, p_wParam, p_lParam);
	}
}

bool D3dApp::isDeviceReady()
{
	switch(g_pD3dDevice->TestCooperativeLevel())
	{
	case D3DERR_DEVICELOST:
		Sleep(50);
		return false;
	case D3DERR_DRIVERINTERNALERROR:
		throw(L"DRIVER INTERNAL ERROR");
		return false;
	case D3DERR_DEVICENOTRESET:
		onLostDevice();
		g_pD3dDevice->Reset(&m_d3dpp);
		onResetDevice();
		return true;
	default:
		return true;
	}
}

void D3dApp::onLostDevice()
{

}

void D3dApp::onResetDevice()
{

}

